#!/usr/bin/python

import pygame
from pygame.locals import *
from pygame import Rect, Color

class Map(object):
	"""This map object controls almost all the game logic"""
	
	def __init__(self, screen, tile_img, monster_images, tower_images, grid):
		
		self.game_started = False 	# flag that indicates if level 1 started
	
		
		# import your wrapper (more global vars)
		self.screen = screen
		self.tile = tile_img
		self.monster_images = monster_images
		self.tower_images = tower_images
		self.grid = grid
		# this saves us processor when needed
		self.width = screen.get_width()
		self.height = screen.get_height()
		
		# based on imported images, creates the grid where game runs
		self.nrows = int(screen.get_height() / tile_img.get_rect().height) 
		self.ncols = int((screen.get_width()-200) / tile_img.get_rect().width) + 1
		
		
		
		self.send = 0			# how many monsters left in this wave
		self.level = 0			# current level
		
		self.monsters = []		# monster instance list
		self.towers = []		# tower instance list
		
		self.wave_wait = [		# milliseconds between creeps in each wave
			2000, 
			2000
			]
			
		self.wave_ammount = [		# ammount of monsters per wave
			20,
			20,
			]
		
		# dummy alarm to work before game starts
		self.creep_spawner_timer = Alarm( 1000, lambda: 'dummy')
		# dummy last tower 
		self.last_tower_built = Dummy()
		self.last_tower_built.placed = True
		
	def game_start(self):
		self.game_started = True
		
		# we set an alarm that handles monster spawning based on time table
		del self.creep_spawner_timer #first we delete dummy alarm
		self.creep_spawner_timer = Alarm(self.wave_wait[0], self.spawn_monster, periodical = True)
		self.start_next_wave()
	
	def start_next_wave(self):
		self.level += 1
		#set timer for creep spawning to new time delta
		self.creep_spawner_timer.set(self.wave_wait[self.level])
		# set creep ammount for this wave according to table
		self.send = self.wave_ammount[self.level]
		
	def _gate_coords(self):
		return (0, self.height / 2)
		
	def spawn_monster(self):
		if self.send != 0:
			self.send -= 1
			self.monsters.append( Monster(
						self.screen,
						self.monster_images[self.level-1],
						len(self.monsters),
						self._gate_coords(),
						self.grid
						))
		else:
			#"no more monsters to spawn!"
			pass
		
	def buy_tower(self, type_num):
		
		# if you have just bought a tower, place that first!
		if self.last_tower_built.placed == False:
			return
			
		new_tower = Tower(self.screen,
				self.tower_images[type_num])
		
		self.towers.append(new_tower)
		self.last_tower_built = new_tower
		
	def place_tower(self):
		self.last_tower_built.place()
		
			
	def draw(self):
		# draw the map (only grass for now)
		
		img_rect = self.tile.get_rect()

		for y in xrange(self.nrows):
			for x in xrange(self.ncols):
				self.screen.blit(self.tile, (x * img_rect.width, y * img_rect.height))
				
#aca va mi intento
			
class grid(object):
	def __init__(self, screen, cell_image):
		self.cell = cell_image
		self.cell_height = cell_image.get_rect().height
		self.cell_width = cell_image.get_rect().width
		self.nrows = int(screen.get_height() / cell_image.get_rect().height) 
		self.ncols = int((screen.get_width()-200) / cell_image.get_rect().width) + 1
		self.grid_mat = []
		for i in xrange(self.nrows):			#this creates a matrix that represents a grid (with 0's)
    			self.grid_mat.append([])		
    			for j in xrange(self.ncols):		
    				self.grid_mat[i].append( 0 )

	def getpos(self, x, y):				#gets the display (real) position (x and y) and returns the grid position
		self.grid_x = 0
		self.grid_y = 0	
		self.height = self.cell_height	
		self.width = self.cell_width
		while True:					#obtains grid 'x' position
			if x >= 0 and x < self.width:		#estos while son medio paja de comentar, si no los entendiste te cuento dps jajaj
				break
			else:
				x = x-self.width
				self.grid_x = self.grid_x + 1	
		while True:					#obtains grid's 'y' position
			if y >= 0 and y < self.height:
				break
			else:
				y = y-self.height
				self.grid_y = self.grid_y + 1					 
		return (self.grid_x, self.grid_y)

	def test(self, a, b):					
		self.p_x = a 					
		self.p_y = b 		
		
		#print 'x,y de la grilla',self.p_x,self.p_y	#esto se puede sacar
		
		# 0 --> if there is no object in the position, you can place one			
		if self.grid_mat[self.p_x][self.p_y] == 0: 
			self.grid_mat[self.p_x][self.p_y] = 1			
			return True
		# 1 --> else, if there is an object in the position, you cant place one
		elif self.grid_mat[self.p_x][self.p_y] == 1:
			return False
		
		
		
class Tower(object):
	""" tower object that the user needs to kill creeps """
	
	def __init__(self, screen, images):
		
		# pixel coordinates of sprite
		self.x = 0
		self.y = 0
		
		# import your environment 
		self.screen = screen
		self.image = images[0]
		self.bullet_image = images[1]
		
		# PARAMETERS
		self.price = 10
		self.damage = 1
		self.speed = 500
		self.range = 100
		
		# flags
		self.placed = False
		self.shooting = False
		
		# list of shots this tower made
		self.shots = []
		
		#dummy timer that will be used to space shots in time
		self.timer = Alarm( 1000, lambda: 'dummy')
		
		
		
	
	def update(self, time_passed):
		
		if not self.placed:
			#if not placed, follow mouse
			x,y = pygame.mouse.get_pos()
			self.x = x
			self.y = y
		else:
			#update shooting timer
			self.timer.update(time_passed)
			
			#update each child shot
			for shot in self.shots:
				shot.update()
				if shot.alive == False:
					del shot
					pass
			
				
		
			
	def draw(self):
		# draw tower and...
		self.screen.blit(self.image, ( self.x - self.image.get_width() / 2, self.y - self.image.get_height() / 2))
		# each child shot
		for shot in self.shots:
			shot.draw()
		
		
	def attack(self, enemy):
		# aims a monster and sets alarm to shoot him
		self.shooting = True
		self.timer = Alarm(self.speed, lambda: self.shoot(enemy), True)
			
	def shoot(self, enemy):
		# if you musn't don't shoot
		if not self.shooting:
			return
		
		# if your enemy is dead or out of range, stop shooting him
		if enemy.alive == False or \
			(enemy.x - self.x)** 2 + (enemy.y - self.y) **2  > self.range **2 :
				
			self.shooting = False
			self.timer = Alarm( 1000, lambda: 'dummy')
			return 
		
		# else, create a new shot and launch it!
		# TODO: shots are never deleted, create timer to erase dead bullets
		new_shot = Shot(self.screen,
				self.bullet_image,
				(self.x,self.y),
				self.damage,
				enemy,
				len(self.shots)
				)
				
		self.shots.append(new_shot)
		
	def place(self):
		if not self.placed:
			print "Tower placed..."
			self.placed = True
		
class Shot(object):
	""" bullets that towers shoot"""
	def __init__(self, screen, image, starting_pos, damage,  objective, id):
		
		# import environment
		self.screen = screen
		self.image = image
		
		# set id (TODO: possibly for deleting it more efficiently)
		self.id = id
		
		#flags
		self.alive = True
		
		# pixel coords
		self.x = starting_pos[0]
		self.y = starting_pos[1]
		
		# PARAMETERS
		self.speed = 5
		self.damage = damage
		self.objective = objective
	
	def update(self):
		# if you haven't hit the target,
		if (self.x - self.objective.x)** 2 + (self.y - self.objective.y) **2 > 10 :
			#... move towards it!
			self.x += (self.objective.x - self.x)/2
			self.y += (self.objective.y - self.y)/2
		else:
			self.explode()
	
	def draw(self):
		if self.alive:
			self.screen.blit(self.image,( self.x - self.image.get_width() / 2, self.y - self.image.get_height() / 2))
		
	def explode(self):
		if self.alive:
			# die and blast your enemy!
			self.alive = False
			self.objective.take_damage(self.damage)
			
			
	
class Monster(object):
	""" Evil instances of the game """
	def __init__(self, screen, image, id, starting_coords, grid):
		
		# TODO: implement pygame built-in sprite object
		#pygame.sprite.Sprite.__init__(self)
		
		# import environment
		self.screen = screen
		self.image = image
		# nose bien donde va esto
		self.grid = grid
		self.veloc = 1#grid.cell_width		
		# set your id
		self.id = id
		
		#pixel coords (get them from your creator as an argument)
		self.x = starting_coords[0]
		self.y = starting_coords[1]
		self.aux_x = starting_coords[0]			# esto es porq nose bien como se manejan las cosas y no se si getpos cuando agarra x,y 
		self.aux_y = starting_coords[1]			# no los modifica
		
		# PARAMETERS
		self.life = 20
		self.speed = self.veloc
		
		#flags
		self.alive = True
		
		
	def update(self):
		# if life depleted, die
		if self.life <= 0:
			self.die()
			
			
		# or advance in the map
		#TODO: integrate A* module to implement pathfinding
		# ACA hice que el malito baje en Y si encuentra algun obstaculo, habria q cambiarlo por el A* 	
		a,b = self.grid.getpos(self.x + self.speed, self.y)	
					
		if self.grid.test(a,b) == False:				#aca tmb hay un tema
			self.x += self.speed
		elif self.grid.test(a,b) == True:
			self.y += self.speed
		
	def draw(self):
		#don't draw yourself if dead
		if self.alive == False:
			return
			
		self.screen.blit(self.image,( self.x - self.image.get_width() / 2, self.y - self.image.get_height() / 2))
		
		# health bar
		health_bar_x = self.x - 7
		health_bar_y = self.y - self.image.get_height() / 2 - 6
		self.screen.fill(   Color('red'), 
				(health_bar_x, health_bar_y, 15, 4))
		self.screen.fill(   Color('green'), 
				(   health_bar_x, health_bar_y, 
				self.life, 4))
		
		
	def take_damage(self, damage):
		# ouch!
		self.life -= damage
		
	def die(self):
		self.alive = False
		
class Alarm(object):
	""" complicated!!!
		object that traces time and executes a function when timer reaches 0"""
	
	def __init__(self, milliseconds, callback, periodical = False):
		
		self.milliseconds = milliseconds	# milliseconds left for firing alarm
		self.callback = callback		# the function to be excecuted when alarm fired
		self.periodical = periodical		# flag that indicates if it has to repeat over and over
		
		if periodical:
			self.period = self.milliseconds
	
		
		self.alive = True
		
	def update(self, time_passed):

		if not self.alive:
			return

		# substract time passed from the remaining milliseconds
		self.milliseconds -= time_passed
		
		# if it's time, call the desired function
		if self.milliseconds <= 0:
			self.callback()
			if self.periodical:
				self.milliseconds += self.period
			else:
				self.alive = False
	
	def set(self, milliseconds):
		#used to reset a timer with other time ammount
		self.alive = True
		self.milliseconds = milliseconds




class Dummy(object):
	""" used to store temporary class variables (attributes) or functions(methods)"""
	pass




def draw_rimmed_box(screen, box_rect, box_color, 
			rim_width=0, 
			rim_color=Color('black')):
	""" Draw a rimmed box on the given surface. The rim is drawn
		outside the box rect.
	"""
	if rim_width:
		rim_rect = Rect(box_rect.left - rim_width,
				box_rect.top - rim_width,
				box_rect.width + rim_width * 2,
				box_rect.height + rim_width * 2)
		pygame.draw.rect(screen, rim_color, rim_rect)
    
	pygame.draw.rect(screen, box_color, box_rect)
	

